﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;

namespace SW2A311_Lib
{
    /// <summary>
    /// Laves til en singleton, så vi altid kun har én database.
    /// </summary>
    public sealed class ProjectCollection : IEnumerable //http://msdn.microsoft.com/en-us/library/ff650316.aspx
    {
        private static readonly ProjectCollection _instance = new ProjectCollection();  // Struktur der understøtter singleton ved multithreading.
        private string FilePath = AppDomain.CurrentDomain.BaseDirectory + "\\projects\\";
        private List<ProjectData> _database;
        public int Count
        {
            get { return _database.Count; }
            private set { }
        }
        /// <summary>
        /// Privat constructor, så man ikke kommer til at lave flere instancer af ProjectCollection.
        /// </summary>
        private ProjectCollection() { _database = new List<ProjectData>(); }

        public static ProjectCollection Instance
        {
            get
            {
                return _instance;
            }
        }
        /// <summary>
        /// Add Project metode med minimalt input.
        /// </summary>
        /// <remarks>
        /// Har check om project allerede eksisterer og om det har et korrekt navn.
        /// </remarks>
        /// <param name="projectName"></param>
        /// <param name="adminName"></param>
        public void Add(string projectName, string adminName)
        {
            if (_database.Exists(x => x.ProjectName == projectName) || string.IsNullOrWhiteSpace(projectName))
            {
                throw new ArgumentException("Project name is either invalid or already exists!");
            }
            else
            {
                ProjectData tempProject = new ProjectData(projectName, adminName);
                _database.Add(tempProject);
            }
        }
        /// <summary>
        /// Add Project metode, med standard input.
        /// </summary>
        /// <remarks>
        /// Har check om project allerede eksisterer og om det har et korrekt navn.
        /// </remarks>
        /// <param name="projectName"></param>
        /// <param name="projectStart"></param>
        /// <param name="projectEnd"></param>
        /// <param name="adminName"></param>
        public void Add(string projectName, DateTime projectStart, DateTime projectEnd, string adminName)
        {
            if (_database.Exists(x => x.ProjectName == projectName) || string.IsNullOrWhiteSpace(projectName))
            {
                throw new ArgumentException("Project name is either invalid or already exists!");
            }
            else
            {
                ProjectData tempProject = new ProjectData(projectName, projectStart, projectEnd, adminName);
                _database.Add(tempProject);
            }
        }
        /// <summary>
        /// Add Project metode der tager et ProjectData object som input.
        /// </summary>
        /// <remarks>
        /// Har check om project har et korrekt navn og om det allerede eksisterer.
        /// </remarks>
        /// <param name="projectData"></param>
        public void Add(ProjectData projectData)
        {
            bool exists = false;

            if (string.IsNullOrWhiteSpace(projectData.ProjectName))
            {
                throw new ArgumentNullException("Project name is invalid!");
            }
            else
            {
                for (int i = 0; i < _database.Count; i++)
                {
                    if (projectData.ProjectName == _database[i].ProjectName)                // Check om project eksisterer hvis det gør overskrives det.
                    {
                        _database[i] = projectData;
                        exists = true;
                    }
                }

                if (!exists)
                {
                    _database.Add(projectData);                                             // Hvis project ikke eksisterer tilføjes det.
                }
            }
        }
        /// <summary>
        /// Fjerner project hvis det eksisterer og RemoveAll returnerer antal elementer fjernet.
        /// </summary>
        /// <param name="projectName"></param>
        public void Remove(string projectName)
        {
            if (_database.RemoveAll(x => x.ProjectName == projectName) == 0)
            {
                throw new ArgumentOutOfRangeException("Project does not exist!");
            }
        }
        /// <summary>
        /// Finder projektet i databasen og tilføjer Task til det.
        /// </summary>
        /// <param name="project"></param>
        /// <param name="task"></param>
        public void AddTaskToProject(ProjectData project, Task task)
        {
            if (task.TaskStart >= project.ProjectStart && task.TaskEnd <= project.ProjectEnd)
                FindProjectByName(project.ProjectName).AddTask(task);
            else
                throw new ArgumentOutOfRangeException("Task timespan must be within project range");

        }
        /// <summary>
        /// Finder projektet i databasen og fjerner Task fra det.
        /// </summary>
        /// <param name="projectName"></param>
        /// <param name="task"></param>
        public void RemoveTaskFromProject(string projectName, Task task)
        {
            FindProjectByName(projectName).DeleteTask(task.TaskName);
        }
        /// <summary>
        /// Laver en ObservableCollection med indholdet fra TaskList og returnerer den.
        /// </summary>
        /// <param name="project"></param>
        /// <returns></returns>
        public ObservableCollection<Task> GetObservableTaskList(ProjectData project)
        {
            return new ObservableCollection<Task>(project.TaskList);
        }
        /// <summary>
        /// Laver en ObservableCollection med indholdet fra _database og returnerer den.
        /// </summary>
        /// <returns></returns>
        public ObservableCollection<ProjectData> GetObservableProjects()
        {
            return new ObservableCollection<ProjectData>(_database);
        }
        /// <summary>
        /// Skriver backup af _database til en fil.
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="project"></param>
        public void WriteDatabaseToFile(string filename, ProjectData project)
        {
            ObjectToFile serializer = new ObjectToFile();
            if (!Directory.Exists(FilePath))
            {
                Directory.CreateDirectory(FilePath);
            }
            string fullFilePath = FilePath + filename + ".txt";
            serializer.SerializeObject<ProjectData>(fullFilePath, project);
        }
        /// <summary>
        /// Indlæser fil med backup og tilføjer det til _database.
        /// </summary>
        /// <remarks>
        /// Hvis filen ikke findes, indlæses intet. 
        /// </remarks>
        public void ReadDatabaseFromFile()
        {
            ObjectToFile serializer = new ObjectToFile();
            try
            {
                if (Directory.Exists(FilePath))
                {
                    foreach (string f in Directory.EnumerateFiles(FilePath))
                        _database.Add(serializer.DeSerializeObject<ProjectData>(f));
                }
            }
            catch (FileNotFoundException)
            {
                // Hvis filen ikke findes, indlæses intet. 
            }
        }
        /// <summary>
        /// Count kaldes på _database og antallet af projekter returneres.
        /// </summary>
        /// <returns></returns>
        public int GetNumberOfElements()
        {
            return _database.Count;
        }
        /// <summary>
        /// Finder index til specifikt project og returnerer det.
        /// </summary>
        /// <param name="project"></param>
        /// <returns></returns>
        public int IndexOf(ProjectData project)
        {
            int i = 0;
            if (project != null)        //  Tjek for null reference
            {
                foreach (ProjectData p in _database)
                {
                    if (p.ProjectName == project.ProjectName)
                        return i;
                    i++;
                }
            }
            return -1;                  // Hvis project ikke eksisterer returneres -1.
        }
        /// <summary>
        /// Finder projektet med en bestemt index i _database og returnerer det. Med bool check om projektet blev fundet. 
        /// </summary>
        /// <param name="i"></param>
        /// <param name="pd"></param>
        /// <returns></returns>
        public bool TryItemAtIndex(int i, out ProjectData pd)
        {
            if (i < _database.Count)
            {
                pd = _database[i];
                return true;
            }
            else
            {
                pd = null;
                return false;
            }
        }
        /// <summary>
        /// Finder project'et og returnerer det.
        /// </summary>
        /// <remarks>
        /// Med check om det har et korrekt navn og om det eksisterer.
        /// </remarks>
        /// <param name="projectName"></param>
        /// <returns></returns>
        public ProjectData FindProjectByName(string projectName)
        {
            if (string.IsNullOrWhiteSpace(projectName))
            {
                throw new ArgumentException("That project name is invalid!");
            }
            else if (_database.Exists(x => x.ProjectName == projectName))
            {
                ProjectData projectByName = null;

                foreach (ProjectData p in _database)
                {
                    if (p.ProjectName == projectName)
                        projectByName = p;
                }
                return projectByName;
            }
            else
            {
                throw new ArgumentException("Project does not exist!");         // Hvis projectet ikke blev fundet.
            }
        }
        /// <summary>
        /// Finder alle projekter der er aktive til dags dato.
        /// </summary>
        /// <remarks>
        /// Er ikke blevet implementeret endnu.
        /// </remarks>
        /// <returns></returns>
        public List<ProjectData> GetActiveProjects()
        {
            List<ProjectData> activeProjects = new List<ProjectData>();
            DateTime timeNow = DateTime.Now;                // Henter dato og tid fra windows.
            foreach (ProjectData p in _database)
            {
                if (p.ProjectStart < timeNow && p.ProjectEnd > timeNow)
                {
                    activeProjects.Add(p);
                }
            }
            return activeProjects;
        }
        /// <summary>
        /// En GetEnumerator så foreach og andre metoder kan få adgang til vores private _database liste.
        /// </summary>
        /// <returns></returns>
        public IEnumerator GetEnumerator()
        {
            return _database.GetEnumerator();
        }
        /// <summary>
        /// Collection metode med sortering, der returnerer 1 objekt og husker hvor den er kommet til.
        /// </summary>
        /// <remarks>
        /// Er ikke implementeret endnu.
        /// </remarks>
        /// <returns></returns>
        public IEnumerable GetEnumeratorSorting()
        {
            IEnumerable<ProjectData> tempList = _database                       // Laver en IEnumerable collection til en midlertidig liste af vores projekter.
                .OrderByDescending(x => x.ProjectStart)                         // Sorterer listen med faldende værdier (ældste project først?).
                .ThenBy(x => x.ProjectName);                                    // Derefter sorteres alfabetisk.
            for (int i = 0; i < _database.Count; i++)
                yield return tempList.ElementAt(i);                             // Til sidst returneres 1 værdi og der huskes hvor den er kommet til.
        }
        /// <summary>
        /// Override af ToString metoden.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            // Override af ToString der skriver alle projekter i _database til en string og returnerer den. Bruges til debugging. 
            string projectString = "";
            foreach (ProjectData p in _database)
            {
                projectString = string.Concat(projectString, "\n", p.ToString());
            }
            return projectString;
        }
    }
}
